﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Phone.Controls;
using PhoneCore.Framework;
using PhoneCore.Framework.Configuration;
using PhoneCore.Framework.IoC;
using PhoneCore.Framework.Views.Command;
using SecureBox.UI.Infrastructure;
using SecureBox.UI.Infrastructure.Data;
using SecureBox.UI.Infrastructure.LongList;
using Record = SecureBox.UI.Infrastructure.Model.Record;
using Keyword = SecureBox.UI.Infrastructure.Model.Keyword;
 
namespace SecureBox.UI.ViewModel
{
    public class SearchViewPageModel : ViewModelBase
    {
        [Dependency]
        public IDataContextService DataContextService { get; set; }

        public SearchViewPageModel() 
        {
           
            GoTo = new ExecuteCommand<object>(DoGoTo);
            SearchActionIconTappedCommand = new ExecuteCommand(DoSearch);
        }

        public bool IsInitialized { get; set; }
        private void Initialize()
        {
            //if some changes occur on data layer, we need to recreate lists
            DataContextService.OnChanged += (o, e) => Update();
            //NOTE: in beginning, filter is empty
            Query = "";
            Update(); 
        }

        /// <summary>
        /// Updates properties
        /// </summary>
        private void Update()
        {
            Query = Query.ToUpper();
            //get records by template
            var records = DataContextService.Records.List();
            RecordsByTemplate = from record in records
                                where record.Name.ToUpper().Contains(Query)
                                group record by record.Template.Name into t
                                orderby t.Key
                                
                                select new PublicGrouping<string, Record>(t);

            //get records by keyword
            var keywordToRecord =
                records.Select(r => r.Keywords.Select(k => new Tuple<Keyword, Record>(k, r)))
                    .Aggregate(new List<Tuple<Keyword, Record>>(), (a, i) =>
                    {
                        a.AddRange(i);
                        return a;
                    });
            RecordsByKeyword = from tuple in keywordToRecord
                               group tuple.Item2 by tuple.Item1.Name into t
                               orderby t.Key
                               where t.Key.ToUpper().Contains(Query)
                               select new PublicGrouping<string, Record>(t);

            //get records by name
            RecordsByName = from record in records
                            where record.Name.ToUpper().Contains(Query)
                            group record by record.Name[0] into t
                            orderby t.Key
                            select new PublicGrouping<char, Record>(t);
            //get records sorted by date
            RecordsByDate = from record in records
                            where record.Name.ToUpper().Contains(Query)
                            orderby record.CreationDate descending
                            group record by GetLabelByTime(record.CreationDate) into t
                            select new PublicGrouping<string, Record>(t);
        }

        /// <summary>
        /// Get date label by date time
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        private string GetLabelByTime(DateTime date)
        {
            var ts = new TimeSpan(DateTime.Now.Ticks - date.Ticks);
            double delta = Math.Abs(ts.TotalSeconds);

            const int SECOND = 1;
            const int MINUTE = 60 * SECOND;
            const int HOUR = 60 * MINUTE;
            const int DAY = 24 * HOUR;
            const int MONTH = 30 * DAY;

            if (delta < 0)
            {
                return "not yet";
            }
            if (delta < 1 * MINUTE)
            {
                return ts.Seconds == 1 ? "one second ago" : ts.Seconds + " seconds ago";
            }
            if (delta < 2 * MINUTE)
            {
                return "a minute ago";
            }
            if (delta < 45 * MINUTE)
            {
                return ts.Minutes + " minutes ago";
            }
            if (delta < 90 * MINUTE)
            {
                return "an hour ago";
            }
            if (delta < 24 * HOUR)
            {
                return ts.Hours + " hours ago";
            }
            if (delta < 48 * HOUR)
            {
                return "yesterday";
            }
            if (delta < 30 * DAY)
            {
                return ts.Days + " days ago";
            }
            if (delta < 12 * MONTH)
            {
                int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30));
                return months <= 1 ? "one month ago" : months + " months ago";
            }
            else
            {
                int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365));
                return years <= 1 ? "one year ago" : years + " years ago";
            }

        }

        /// <summary>
        /// Go to selected record
        /// </summary>
        /// <param name="args"></param>
        private void DoGoTo(object args)
        {
            //TODO use different approach
            var record = ((args as LongListSelector).SelectedItem as Record);
            if (record == null)
                return;
            (args as LongListSelector).SelectedItem = null;

            NavigationService.NavigateTo(record.Template.Name,
                new Dictionary<string, object>() { { "RecordId", record.Id } });

        }

        public void DoSearch()
        {
            Update();
        }


        #region Command properties

        public ExecuteCommand SearchActionIconTappedCommand
        {
            get;
            private set;
        }

        public ExecuteCommand<object> GoTo
        {
            get;
            private set;
        }

        #endregion

        public string Query { get; set; }

        #region ItemSources

        private IEnumerable<PublicGrouping<string, Record>> _recordsByTemplate;
        public  IEnumerable<PublicGrouping<string, Record>> RecordsByTemplate
        {
            get { return _recordsByTemplate; }
            set { _recordsByTemplate = value; RaisePropertyChanged("RecordsByTemplate"); }
        }

        private IEnumerable<PublicGrouping<string, Record>> _recordsByKeyword;
        public IEnumerable<PublicGrouping<string, Record>> RecordsByKeyword
        {
            get { return _recordsByKeyword; }
            set { _recordsByKeyword = value; RaisePropertyChanged("RecordsByKeyword"); }
        }

        private IEnumerable<PublicGrouping<char, Record>> _recordsByName;
        public IEnumerable<PublicGrouping<char, Record>> RecordsByName
        {
            get { return _recordsByName; }
            set { _recordsByName = value; RaisePropertyChanged("RecordsByName"); }
        }

        private IEnumerable<PublicGrouping<string, Record>> _recordsByDate;
        public IEnumerable<PublicGrouping<string, Record>> RecordsByDate
        {
            get { return _recordsByDate; }
            set { _recordsByDate = value; RaisePropertyChanged("RecordsByDate"); }
        }

        #endregion

        public override void LoadStateFrom(IDictionary<string, object> state)
        {
            if(!IsInitialized)
                Initialize();
            base.LoadStateFrom(state);
        }
    }
}
